// Copyright 2011-2012, Vanya Davidenko.
// Используемая кодировка: UTF-8.

#ifndef LIBV_BOOST_XML_XML_NODE_H_
#define LIBV_BOOST_XML_XML_NODE_H_

#include <string>
#include <libv/check.h>
#include <libv/boost/xml/xml_attribute.h>
#include "rapidxml.h"



namespace libv {

// Обертка для чтения rapidxml::xml_node, имеющая специальное нулевое состояние,
// используемое для обозначения отсутствия узла. Любое обращение к такому
// узлу возвращает его же. Это позволяет выполнять сложные запросы типа
// node.Child("1").Child("2").Child("3"), не проверяя отсутствие дочернего
// узла на каждом шаге.
// Объект поддерживает итерацию по дочерним узлам и атрибутам.
// (!) Итераторы возвращают временные объекты,
//     которые нужно копировать, а не сохранять по ссылке.
//
// Класс не потокобезопасен.
// Методы не генерируют исключений, кроме libv::PrecondException
// при несоблюдении предусловий.
class XmlNode {
  friend class XmlDocument;
  friend class XmlAttribute;
  public:
    class AttributesIterator;  // const-итератор

    class Iterator;
    typedef Iterator iterator;
    typedef Iterator const_iterator;

    class DepthFirstIterator;


    XmlNode()
        : pimpl_(NULL) {}

    // Флаг, обозначающий нулевое состояние объекта.
    bool IsNull() const {
        return this->pimpl_ == NULL;
    }


    bool operator==(const XmlNode& rhv) const {
        if ( IsNull() || rhv.IsNull() ) {
            return IsNull() == rhv.IsNull();
        }
        return pimpl_ == rhv.pimpl_;
    }

    bool operator!=(const XmlNode& rhv) const {
        return !(*this == rhv);
    }


    // Имя тега.
    // @pre !IsNull()
    ::std::string Name() const {
        LIBV_PRECOND(!IsNull());
        if ( pimpl_->name_size() == 0 ) {
            return ::std::string();
        }
        return ::std::string(pimpl_->name(), pimpl_->name_size());
    }

    // Содержимое тега.
    // @pre !IsNull()
     ::std::string Value() const {
        LIBV_PRECOND(!IsNull());
        if ( pimpl_->value_size() == 0 ) {
            return ::std::string();
        }
        return ::std::string(pimpl_->value(), pimpl_->value_size());
    }


    // Получение родителя узла.
    // Метод может вернуть нулевой объект, в случае если данный узел
    // является корневым.
    // @pre IsNull() == false.
    const XmlNode Parent() const {
        LIBV_PRECOND(!IsNull());
        return XmlNode(pimpl_->parent());
    }

    bool HasParent() const {
        return pimpl_->parent() != NULL;
    }


    // Получение первого дочернего узла с заданным именем.
    // Метод возвращает нулевой объект при отсутствии запрашиваемого узла.
    // Для нулевого объекта метод возвращает нулевой объект.
    const XmlNode Child(const char* name) const {
        if ( IsNull() ) {
            return XmlNode(NULL);
        }
        return XmlNode(pimpl_->first_node(name));
    }
    const XmlNode Child(const ::std::string& name) const {
        return Child(name.c_str());
    }
    const XmlNode operator[](const char* name) const {
        return Child(name);
    }
    const XmlNode operator[](const ::std::string& name) const {
        return Child(name);
    }

    bool HasChild(const char* name) const {
        return !IsNull() && pimpl_->first_node(name) != NULL;
    }
    bool HasChild(const ::std::string& name) const {
        return HasChild(name.c_str());
    }


    // Создание итератора по дочерним узлам.
    // Циклическая зависимость. Реализация в конце файла.
    inline Iterator ChildrenBegin() const;
    inline Iterator ChildrenEnd() const;

    // Методы-псевдонимы для foreach.
    inline const_iterator begin() const;
    inline const_iterator end() const;


    // Получение первого атрибута с заданным именем.
    // Метод возвращает нулевой объект при отсутствии запрашиваемого узла.
    // Для нулевого объекта метод возвращает нулевой объект.
    const XmlAttribute Attribute(const char* name) const {
        if ( IsNull() ) {
            return XmlAttribute(NULL, new XmlNode());
        }
        return XmlAttribute(pimpl_->first_attribute(name), new XmlNode(*this));
    }
    const XmlAttribute Attribute(const ::std::string& name) const {
        return Attribute(name.c_str());
    }

    bool HasAttribute(const char* name) const {
        return !IsNull() && pimpl_->first_attribute(name) != NULL;
    }
    bool HasAttribute(const ::std::string& name) const {
        return HasAttribute(name.c_str());
    }

    // Создание итератора по атрибутам.
    inline AttributesIterator AttributesBegin() const;
    inline AttributesIterator AttributesEnd() const;

  private:
    explicit XmlNode(const rapidxml::xml_node<>* pimpl)
      : pimpl_(pimpl) {}

    const rapidxml::xml_node<>* pimpl_;
};

}  // libv



#include "internal/xml_node_iterator.hpp"
#include "internal/xml_atributes_iterator.hpp"

namespace libv {

XmlNode::Iterator XmlNode::ChildrenBegin() const {
    if ( IsNull() ) {
        return ChildrenEnd();
    }
    return Iterator(*this);
}
XmlNode::Iterator XmlNode::ChildrenEnd() const {
    return Iterator();
}

XmlNode::const_iterator XmlNode::begin() const {
    return ChildrenBegin();
}
XmlNode::const_iterator XmlNode::end() const {
    return ChildrenEnd();
}

XmlNode::AttributesIterator XmlNode::AttributesBegin() const {
    if ( IsNull() ) {
        return AttributesEnd();
    }
    return AttributesIterator(*this);
}
XmlNode::AttributesIterator XmlNode::AttributesEnd() const {
    return AttributesIterator();
}

}  // libv

#endif  // LIBV_BOOST_XML_XML_NODE_H_
